{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 灵活的数据读取"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from torchvision.datasets import ImageFolder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 三个文件夹，每个文件夹一共有 3 张图片作为例子\n",
    "folder_set = ImageFolder('./example_data/image/')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'class_1': 0, 'class_2': 1, 'class_3': 2}"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看名称和类别下标的对应\n",
    "folder_set.class_to_idx"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[('./example_data/image/class_1/1.png', 0),\n",
       " ('./example_data/image/class_1/2.png', 0),\n",
       " ('./example_data/image/class_1/3.png', 0),\n",
       " ('./example_data/image/class_2/10.png', 1),\n",
       " ('./example_data/image/class_2/11.png', 1),\n",
       " ('./example_data/image/class_2/12.png', 1),\n",
       " ('./example_data/image/class_3/16.png', 2),\n",
       " ('./example_data/image/class_3/17.png', 2),\n",
       " ('./example_data/image/class_3/18.png', 2)]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 得到所有的图片名字和标签\n",
    "folder_set.imgs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 取出其中一个数据\n",
    "im, label = folder_set[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\nAABJRU5ErkJggg==\n",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=32x32 at 0x11439EA90>"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "im"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "label"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from torchvision import transforms as tfs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 传入数据预处理方式\n",
    "data_tf = tfs.ToTensor()\n",
    "\n",
    "folder_set = ImageFolder('./example_data/image/', transform=data_tf)\n",
    "\n",
    "im, label = folder_set[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\n",
       "(0 ,.,.) = \n",
       "  0.2314  0.1686  0.1961  ...   0.6196  0.5961  0.5804\n",
       "  0.0627  0.0000  0.0706  ...   0.4824  0.4667  0.4784\n",
       "  0.0980  0.0627  0.1922  ...   0.4627  0.4706  0.4275\n",
       "           ...             ⋱             ...          \n",
       "  0.8157  0.7882  0.7765  ...   0.6275  0.2196  0.2078\n",
       "  0.7059  0.6784  0.7294  ...   0.7216  0.3804  0.3255\n",
       "  0.6941  0.6588  0.7020  ...   0.8471  0.5922  0.4824\n",
       "\n",
       "(1 ,.,.) = \n",
       "  0.2431  0.1804  0.1882  ...   0.5176  0.4902  0.4863\n",
       "  0.0784  0.0000  0.0314  ...   0.3451  0.3255  0.3412\n",
       "  0.0941  0.0275  0.1059  ...   0.3294  0.3294  0.2863\n",
       "           ...             ⋱             ...          \n",
       "  0.6667  0.6000  0.6314  ...   0.5216  0.1216  0.1333\n",
       "  0.5451  0.4824  0.5647  ...   0.5804  0.2431  0.2078\n",
       "  0.5647  0.5059  0.5569  ...   0.7216  0.4627  0.3608\n",
       "\n",
       "(2 ,.,.) = \n",
       "  0.2471  0.1765  0.1686  ...   0.4235  0.4000  0.4039\n",
       "  0.0784  0.0000  0.0000  ...   0.2157  0.1961  0.2235\n",
       "  0.0824  0.0000  0.0314  ...   0.1961  0.1961  0.1647\n",
       "           ...             ⋱             ...          \n",
       "  0.3765  0.1333  0.1020  ...   0.2745  0.0275  0.0784\n",
       "  0.3765  0.1647  0.1176  ...   0.3686  0.1333  0.1333\n",
       "  0.4549  0.3686  0.3412  ...   0.5490  0.3294  0.2824\n",
       "[torch.FloatTensor of size 3x32x32]"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "im"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "label"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看到通过这种方式能够非常方便的访问每个数据点"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from torch.utils.data import Dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 定义一个子类叫 custom_dataset，继承与 Dataset\n",
    "class custom_dataset(Dataset):\n",
    "    def __init__(self, txt_path, transform=None):\n",
    "        self.transform = transform # 传入数据预处理\n",
    "        with open(txt_path, 'r') as f:\n",
    "            lines = f.readlines()\n",
    "        \n",
    "        self.img_list = [i.split()[0] for i in lines] # 得到所有的图像名字\n",
    "        self.label_list = [i.split()[1] for i in lines] # 得到所有的 label \n",
    "\n",
    "    def __getitem__(self, idx): # 根据 idx 取出其中一个\n",
    "        img = self.img_list[idx]\n",
    "        label = self.label_list[idx]\n",
    "        if self.transform is not None:\n",
    "            img = self.transform(img)\n",
    "        return img, label\n",
    "\n",
    "    def __len__(self): # 总数据的多少\n",
    "        return len(self.label_list)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "txt_dataset = custom_dataset('./example_data/train.txt') # 读入 txt 文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1009_2.png\n",
      "YOU\n"
     ]
    }
   ],
   "source": [
    "# 取得其中一个数据\n",
    "data, label = txt_dataset[0]\n",
    "print(data)\n",
    "print(label)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1046_7.png\n",
      "LIFE\n"
     ]
    }
   ],
   "source": [
    "# 再取一个\n",
    "data2, label2 = txt_dataset[34]\n",
    "print(data2)\n",
    "print(label2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "所以通过这种方式我们也能够非常方便的定义一个数据读入，同时也能够方便的定义数据预处理"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## DataLoader"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from torch.utils.data import DataLoader"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "train_data1 = DataLoader(folder_set, batch_size=2, shuffle=True) # 将 2 个数据作为一个 batch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " 1\n",
      " 2\n",
      "[torch.LongTensor of size 2]\n",
      "\n",
      "\n",
      " 0\n",
      " 1\n",
      "[torch.LongTensor of size 2]\n",
      "\n",
      "\n",
      " 0\n",
      " 2\n",
      "[torch.LongTensor of size 2]\n",
      "\n",
      "\n",
      " 0\n",
      " 2\n",
      "[torch.LongTensor of size 2]\n",
      "\n",
      "\n",
      " 1\n",
      "[torch.LongTensor of size 1]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "for im, label in train_data1: # 访问迭代器\n",
    "    print(label)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看到，通过训练我们可以访问到所有的数据，这些数据被分为了 5 个 batch，前面 4 个都有两个数据，最后一个 batch 只有一个数据，因为一共有 9 个数据，同时顺序也被打乱了"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面我们用自定义的数据读入举例子"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "train_data2 = DataLoader(txt_dataset, 8, True) # batch size 设置为 8"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "im, label = next(iter(train_data2)) # 使用这种方式访问迭代器中第一个 batch 的数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('377_10.png',\n",
       " '178_1.png',\n",
       " '5008_4.png',\n",
       " '5050_5.png',\n",
       " '716_3.png',\n",
       " '415_8.png',\n",
       " '858_6.png',\n",
       " '5086_10.png')"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "im"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('AUGUST',\n",
       " 'OTKRIJTE',\n",
       " 'ASTAIRE',\n",
       " 'BOONMEE',\n",
       " 'OF',\n",
       " 'CAUTION',\n",
       " 'PROPANE',\n",
       " 'PECC')"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "label"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在有一个需求，希望能够将上面一个 batch 输出的 label 补成相同的长度，短的 label 用 0 填充，我们就需要使用 `collate_fn` 来自定义我们 batch 的处理方式，下面直接举例子"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def collate_fn(batch):\n",
    "    batch.sort(key=lambda x: len(x[1]), reverse=True) # 将数据集按照 label 的长度从大到小排序\n",
    "    img, label = zip(*batch) # 将数据和 label 配对取出\n",
    "    # 填充\n",
    "    pad_label = []\n",
    "    lens = []\n",
    "    max_len = len(label[0])\n",
    "    for i in range(len(label)):\n",
    "        temp_label = label[i]\n",
    "        temp_label += '0' * (max_len - len(label[i]))\n",
    "        pad_label.append(temp_label)\n",
    "        lens.append(len(label[i]))\n",
    "    pad_label \n",
    "    return img, pad_label, lens # 输出 label 的真实长度"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用我们自己定义 collate_fn 看看效果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "train_data3 = DataLoader(txt_dataset, 8, True, collate_fn=collate_fn) # batch size 设置为 8"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "im, label, lens = next(iter(train_data3))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('5016_1.png',\n",
       " '2314_3.png',\n",
       " '731_9.png',\n",
       " '5019_4.png',\n",
       " '208_4.png',\n",
       " '5017_12.png',\n",
       " '5190_1.png',\n",
       " '855_12.png')"
      ]
     },
     "execution_count": 93,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "im"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['LINDSAY',\n",
       " 'ADDRESS',\n",
       " 'MAIDEN0',\n",
       " 'EINER00',\n",
       " 'INDIA00',\n",
       " 'GERE000',\n",
       " 'JAWS000',\n",
       " 'TD00000']"
      ]
     },
     "execution_count": 94,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "label"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[7, 7, 6, 5, 5, 4, 4, 2]"
      ]
     },
     "execution_count": 95,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lens"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看到一个 batch 中所有的 label 都从长到短进行排列，同时短的 label 都被补长了，所以使用 collate_fn 能够非常方便的处理一个 batch 中的数据，一般情况下，没有特别的要求，使用 pytorch 中内置的 collate_fn 就可以满足要求了"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "mx",
   "language": "python",
   "name": "mx"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
